package com.jdy.processor;


import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;

import javax.lang.model.element.Modifier;
import java.util.Objects;
import java.util.Set;

/**
 * Spring 框架学习
 * <p>
 * [Description]
 * <p>
 * 创建人 Dale 时间 2020/12/15 13:49
 */
class Utils {

    static final String THIS = "this";

    static final String SET = "set";

    static final String GET = "get";

    /**
     * 创建建造者的静态方法名
     */
    static final String BUILDER_STATIC_METHOD_NAME = "builder";

    /**
     * 建造方法名
     */
    static final String BUILD_METHOD_NAME = "build";

    /**
     * 构造方法名字，比较特殊
     */
    static final String CONSTRUCTOR_NAME = "<init>";

    /**
     * 克隆一个字段的语法树节点，该节点作为方法的参数
     * 具有位置信息的语法树节点是不能复用的！
     *
     * @param treeMaker    语法树节点构造器
     * @param variableDecl 字段的语法树节点
     * @return 方法参数的语法树节点
     */
    static JCTree.JCVariableDecl cloneVariableDecl(TreeMaker treeMaker, JCVariableDecl variableDecl) {
//        Type arrayType = types.makeArrayType(symtab.byteType);
//        VarSymbol varSymbol = new VarSymbol(variableDecl.getModifiers().flags, variableDecl.name, arrayType, symtab.arrayClass);
//        return treeMaker.VarDef(varSymbol, variableDecl.init);
        return treeMaker.VarDef(
                variableDecl.getModifiers(), //访问标志。极其坑爹！！！
                variableDecl.name, //名字
                variableDecl.vartype, //类型
                variableDecl.init //初始化语句
        );
    }

    /**
     * 克隆一个字段的语法树节点集合，作为方法的参数列表
     *
     * @param treeMaker     语法树节点构造器
     * @param variableDecls 字段的语法树节点集合
     * @return 方法参数的语法树节点集合
     */
    static List<JCVariableDecl> cloneVariableDecl(TreeMaker treeMaker, List<JCVariableDecl> variableDecls) {
        ListBuffer<JCVariableDecl> jcVariables = new ListBuffer<>();

        for (JCVariableDecl jcVariable : variableDecls) {
            jcVariables.append(cloneVariableDecl(treeMaker, jcVariable));
        }

        return jcVariables.toList();
    }

    /**
     * 判断是否是合法的字段
     *
     * @param tree 语法树节点
     * @return 是否是合法字段
     */
    private static boolean isValidField(JCTree tree) {
        if (Objects.equals(JCTree.Kind.VARIABLE, tree.getKind())) {
            JCVariableDecl jcVariable = (JCVariableDecl) tree;
            Set<Modifier> flags = jcVariable.mods.getFlags();
//            for (Modifier flag : flags) {
//                //排除掉静态方法和final方法
//                if (Objects.equals(flag, Modifier.STATIC) || Objects.equals(flag, Modifier.FINAL)) {
//                    return false;
//                }
//            }
            return !(flags.contains(Modifier.STATIC) || flags.contains(Modifier.FINAL));
        }
        return true;
    }


    /**
     * 获取字段的语法树节点的集合
     *
     * @param classDecl 类的语法树节点
     * @return 字段的语法树节点的集合
     */
    static List<JCVariableDecl> getJCVariables(JCClassDecl classDecl) {
        ListBuffer<JCVariableDecl> jcVariables = new ListBuffer<>();

        //遍历jcClass的所有内部节点，可能是字段，方法等等
        for (JCTree tree : classDecl.defs) {
            //找出所有set方法节点，并添加
            if (isValidField(tree)) {
                //注意这个com.sun.tools.javac.util.List的用法，不支持链式操作，更改后必须赋值
                jcVariables.append((JCTree.JCVariableDecl) tree);
            }
        }

        return jcVariables.toList();
    }

    /**
     * 判断是否为set方法
     *
     * @param tree 语法树节点
     * @return 判断是否是Set方法
     */
    static boolean isSetMethod(JCTree tree) {
        if (Objects.equals(JCTree.Kind.METHOD, tree.getKind())) {
            JCMethodDecl methodDecl = (JCMethodDecl) tree;
            return methodDecl.name.toString().startsWith(SET)
                    && methodDecl.params.size() == 1
                    && !methodDecl.mods.getFlags().contains(Modifier.STATIC);
        }
        return false;
    }

    /**
     * 提取出所有set方法的语法树节点
     *
     * @param jcClass 类的语法树节点
     * @return set方法的语法树节点的集合
     */
    static List<JCMethodDecl> getSetJCMethods(JCClassDecl jcClass) {
        ListBuffer<JCMethodDecl> methodDecls = new ListBuffer<>();

        //遍历jcClass的所有内部节点，可能是字段，方法等等
        for (JCTree tree : jcClass.defs) {
            //找出所有set方法节点，并添加
            if (isSetMethod(tree)) {
                //注意这个com.sun.tools.javac.util.List的用法，不支持链式操作，更改后必须赋值
                methodDecls.append((JCMethodDecl) tree);
            }
        }

        return methodDecls.toList();
    }


    /**
     * 判断是否存在无参构造方法
     *
     * @param classDecl 类的语法树节点
     * @return 是否存在
     */
    static boolean hasNoArgsConstructor(JCClassDecl classDecl) {
        for (JCTree tree : classDecl.defs) {
            if (!Objects.equals(Kind.METHOD, tree.getKind()))
                continue;

            JCMethodDecl methodDecl = (JCMethodDecl) tree;

            if (!Objects.equals(CONSTRUCTOR_NAME, methodDecl.name.toString()))
                continue;

            if (methodDecl.params.isEmpty()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否存在全参的构造方法
     *
     * @param variableDecls 字段的语法树节点集合
     * @param classDecl     类的语法树节点
     * @return 是否存在
     */
    static boolean hasAllArgsConstructor(List<JCVariableDecl> variableDecls, JCClassDecl classDecl) {
        for (JCTree tree : classDecl.defs) {
            if (!Objects.equals(Kind.METHOD, tree.getKind()))
                continue;

            JCMethodDecl methodDecl = (JCMethodDecl) tree;
            if (!Objects.equals(CONSTRUCTOR_NAME, methodDecl.name.toString()))
                continue;

            if (variableDecls.size() != methodDecl.params.size())
                continue;

            for (int i = 0; i < variableDecls.size(); i++) {
                if (!variableDecls.get(i).vartype.type.equals(methodDecl.params.get(i).vartype.type)) {
                    return false;
                }
            }

            return true;
        }
        return false;
    }


    /**
     * 判断是否存在指定字段的set方法，返回类型不作为判断依据，因为Java中方法重载与返回类型无关
     *
     * @param variableDecl 字段的语法树节点
     * @param classDecl    类的语法树节点
     * @return 是否存在
     */
    static boolean hasSetMethod(JCVariableDecl variableDecl, JCClassDecl classDecl) {
        String methodName = fromMethodName(SET, variableDecl.name.toString());
        for (JCTree tree : classDecl.defs) {
            if (!Objects.equals(Kind.METHOD, tree.getKind()))
                continue;
            JCMethodDecl methodDecl = (JCMethodDecl) tree;
            if (methodName.equals(methodDecl.name.toString())
                    && methodDecl.params.size() == 1
                    && methodDecl.params.get(0).vartype.type.equals(variableDecl.vartype.type)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否存在指定字段的get方法，返回类型不作为判断依据，因为Java中方法重载与返回类型无关
     *
     * @param variableDecl 字段的语法树节点
     * @param classDecl    类的语法树节点
     * @return 是否存在
     */
    static boolean hasGetMethod(JCVariableDecl variableDecl, JCClassDecl classDecl) {
        String methodName = fromMethodName(GET, variableDecl.name.toString());
        for (JCTree tree : classDecl.defs) {
            if (!Objects.equals(Kind.METHOD, tree.getKind()))
                continue;
            JCMethodDecl methodDecl = (JCMethodDecl) tree;
            if (methodName.equals(methodDecl.name.toString())
                    && methodDecl.params.size() == 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 字段名转换为set方法名
     *
     * @param fieldName 字段名
     * @return set方法名
     */
    static String fromMethodName(String begin, String fieldName) {
        return begin + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    public static boolean hasMethod(Name methodName, List<JCVariableDecl> parameters, JCTree jcTree) {
        JCClassDecl classDecl = (JCClassDecl) jcTree;

        for (JCTree tree : classDecl.defs) {
            if (!Objects.equals(Kind.METHOD, tree.getKind()))
                continue;

            JCMethodDecl methodDecl = (JCMethodDecl) tree;

            if (!Objects.equals(methodName, methodDecl.name))
                continue;

            List<JCVariableDecl> list = methodDecl.getParameters();
            if (list.size() != parameters.size())
                continue;

            for (int i = 0; i < list.size(); i++) {
                JCVariableDecl current = list.get(i);
                JCVariableDecl target = parameters.get(i);

                if (Objects.equals(current.vartype.toString(), target.vartype.toString()))
                    return true;
            }
        }

        return false;
    }
}
